package com.lntea.netty.demo.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class ChatRoomServer {
    
    private Selector selector;
    
    private SelectionKey sk;
    
    private int port = 9999;
    
    private Charset charset = Charset.forName("utf-8");
    
    private static final String USER_CONTENT_SPLIT = "#@#";

    private static final String USER_EXIST = "user existed";
    
    private List<String> userNameList = new ArrayList<String>();
    
    public static void main(String[] args) {
        new ChatRoomServer().init();
    }
    
    public void init(){
        try {
            selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(port));
            serverSocketChannel.configureBlocking(false);
            sk = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            
            while(true){
                int readyNum = selector.select();
                if(readyNum==0){
                    continue;
                }
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> ite = keys.iterator();
                if(ite.hasNext()){
                    SelectionKey key = ite.next();
                    ite.remove();
                    processSelectionKey(serverSocketChannel,key);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }

    private void processSelectionKey(ServerSocketChannel serverSocketChannel, SelectionKey key) throws IOException {
        if(key.isAcceptable()){
            SocketChannel sc = serverSocketChannel.accept();
            sc.configureBlocking(false);
            sc.register(selector, SelectionKey.OP_READ);
            
            sk.interestOps(SelectionKey.OP_ACCEPT);
            System.out.println("Server is listening from client:"+sc.getRemoteAddress());
            sc.write(charset.encode("please input your name:"));
        }
        if(key.isReadable()){
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            SocketChannel channel = (SocketChannel) key.channel();
            
            int len = -1;
            StringBuffer sb = new StringBuffer();
            while((len=channel.read(buffer))>0){
                buffer.flip();
                sb.append(new String(buffer.array(),0,len));
            }
            
            System.out.println("server is listening from client:"+channel.getRemoteAddress()+",data receive:"+sb.toString());
            key.interestOps(SelectionKey.OP_READ);
            
            if(sb.length()>0){
                String[] arrayContent = sb.toString().split(USER_CONTENT_SPLIT);
                if(arrayContent!=null && arrayContent.length == 1){
                    String name = arrayContent[0];
                    if(userNameList.contains(name)){
                        channel.write(charset.encode(USER_EXIST));
                    }else{
                        userNameList.add(name);
                        int onlineNum = onlineNum(selector);
                        String message = "welcome " + name + " join chat room. Online numbers:"+onlineNum;
                        broadcast(selector,null,message);
                    }
                }else if(arrayContent!=null && arrayContent.length > 1){
                    String name = arrayContent[0];
                    String message = arrayContent[1];
                    message = name + " say " + message;
                    if(userNameList.contains(name)){
                        broadcast(selector, channel, message);
                    }
                }
            }
        }
    }

    private void broadcast(Selector selector2, SocketChannel except, String message) throws IOException {
        Set<SelectionKey> keys = selector.keys();
        for(SelectionKey key : keys){
            SelectableChannel channel = key.channel();
            if(channel instanceof SocketChannel && channel != except){
                SocketChannel dest = (SocketChannel) channel;
                dest.write(charset.encode(message));
            }
        }
    }

    private int onlineNum(Selector selector) {
        int res = 0;
        Set<SelectionKey> keys = selector.keys();
        for(SelectionKey key : keys){
            SelectableChannel channel = key.channel();
            if(channel instanceof SocketChannel){
                res++;
            }
        }
        return res;
    }
}